﻿using CommServer.Cache;
using CommServer.Common;
using CommServer.Tasks;
using CommServer.Tasks.Common;
using CommServer.Upgrade;
using Microsoft.AspNetCore.Hosting;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace CommServer
{
    public class AppManager
    {
        private static upgradeMng m_upgradeMng = new upgradeMng();
        private static AppManager _appManager;
        private IWebHost _web;
        private CancellationTokenSource _tokenSource;
        private bool _running;
        private bool _restart;
        private int _devicePort = 9999;
        private int _httpPort = 10000;
        public bool Restarting => _restart;
        private static Task_DataSave m_Task_DataSave = new Task_DataSave(DateTime.Now, 5, IntervalUnit.Second);
        private static DeviceServer m_DeviceServer = new DeviceServer();
        private Action<string> onConnect;
        private Action<string> onLogin;
        private Action<string, string> onDisconnect;
        private Action<dynamic> onInvDataReceive;
        private Action<dynamic> onMeterDataReceive;
        private Action<List<dynamic>> onAalarmReceive;
        public AppManager(int devicePort,int HttpPort)
        {
            _running = false;
            _restart = false;
            _devicePort = devicePort;
            _httpPort = HttpPort;
            initEvent();
        }
        public bool Running => _running;
        public static AppManager Load(int devicePort, int HttpPort)
        {
            if (_appManager == null)
                _appManager = new AppManager(devicePort,HttpPort);

            return _appManager;
        }
        public IWebHost Web => _web;
        public void Start()
        {
            if (_running)
                return;

            if (_tokenSource != null && _tokenSource.IsCancellationRequested)
                return;
            StartDeviceServer(_devicePort);
            m_Task_DataSave.Start();

            _tokenSource = new CancellationTokenSource();
            _tokenSource.Token.ThrowIfCancellationRequested();
            _running = true;

            _web = new WebHostBuilder()
            .UseKestrel()
                    .UseContentRoot(Directory.GetCurrentDirectory())
                    .UseIISIntegration()
                    .UseUrls(new string[] { "http://*:" + _httpPort })
                    .UseStartup<Startup>()
                    .UseApplicationInsights()
                    .Build();
            _web.RunAsync(_tokenSource.Token);
        }
        private void initEvent()
        {
            m_DeviceServer.OnConnect += new Action<string>((SessionId) =>
            {
                Log.Info("SessionId=" + SessionId + " Connected");
            });
            m_DeviceServer.OnLogin += new Action<string,string>((EccAddr,Pwd) =>
            {
                Log.Info("EccAddr=" + EccAddr + " Login");
                if((Pwd!=null)&&(Pwd.Length>0))
                {
                    dynamic invInfo = new ExpandoObject();
                    invInfo.SN = EccAddr;
                    invInfo.Pwd = Pwd;
                    m_Task_DataSave.AddInvInfo(invInfo);
                }
            });
            m_DeviceServer.OnDisConnect += new Action<string, string>((SessionId, EccAddr) =>
            {
                Log.Info("SessionId=" + SessionId + " Disconnect;EccAddr=" + EccAddr);
            });
            m_DeviceServer.OnInvDataReceive += new Action<dynamic>((Data) =>
            {
                DateTime LastTime = CacheDevices.GetLastInvTime(Data.RtuSN, Data.Addr);
                if (LastTime == Data.WriteTime)
                {
                    Log.Info(Data.RtuSN, "inv addr=" + Data.Addr + ",Time=" + Data.WriteTime.ToString("yyyy-MM-dd HH:mm:ss") + ";Duplicate record ignore");
                    return;
                }


                CacheDevices.UpdateLastInvTime(Data.RtuSN, Data.Addr, Data.WriteTime);
                m_Task_DataSave.AddInvRealData(Data);
            });
            m_DeviceServer.OnSensorDataReceive += new Action<dynamic>((Data) =>
            {
                DateTime LastTime = CacheDevices.GetLastSensorTime(Data.RtuSN, Data.Addr);
                if (LastTime == Data.WriteTime)
                {
                    Log.Info(Data.RtuSN, "Sensor addr=" + Data.Addr + ",Time=" + Data.WriteTime.ToString("yyyy-MM-dd HH:mm:ss") + ";Duplicate record ignore");
                    return;
                }


                CacheDevices.UpdateLastSensorTime(Data.RtuSN, Data.Addr, Data.WriteTime);
                m_Task_DataSave.AddSensorRealData(Data);
            });
            m_DeviceServer.OnMeterDataReceive += new Action<dynamic>((Data) =>
            {
                DateTime LastTime = CacheDevices.GetLastMeterTime(Data.RtuSN, Data.Addr);
                if (LastTime == Data.WriteTime)
                {
                    Log.Info(Data.RtuSN, "meter addr=" + Data.Addr + ",Time=" + Data.WriteTime.ToString("yyyy-MM-dd HH:mm:ss") + ";Duplicate record ignore");
                    return;
                }


                CacheDevices.UpdateLastMeterTime(Data.RtuSN, Data.Addr, Data.WriteTime);

                m_Task_DataSave.AddMeterRealData(Data);
            });
            m_DeviceServer.OnAlarmDataReceive += new Action<List<dynamic>>((Data) =>
            {
                List<dynamic> Alarms = new List<dynamic>();
                foreach (var Alarm in Data)
                {
                    DateTime LastTime = CacheDevices.GetLastAlarmTime(Alarm.RtuSN, Alarm.DevAddr, Alarm.Note);
                    if (LastTime == Alarm.LogTime)
                    {
                        Log.Info(Alarm.RtuSN, "alarm addr=" + Alarm.DevAddr + ",Note=" + Alarm.Note + ",Time=" + Alarm.LogTime.ToString("yyyy-MM-dd HH:mm:ss") + ";Duplicate record ignore");
                        continue;
                    }


                    CacheDevices.UpdateLastAlarmTime(Alarm.RtuSN, Alarm.DevAddr, Alarm.Note, Alarm.LogTime);
                    Alarms.Add(Alarms);
                }
                if (Alarms.Count > 0)
                {
                    m_Task_DataSave.AddAlarmData(Data);
                }
            });
        }
        private  void StartDeviceServer(int DevicePort)
        {
           
            m_DeviceServer.Start(DevicePort);
            m_upgradeMng.start();
        }
        private void StopDeviceServer()
        {
            m_DeviceServer.Stop();
            m_upgradeMng.stop();
        }
        public DeviceServer GetDeviceServer()
        {
            return m_DeviceServer;
        }
        public void Stop()
        {
            if (!_running)
                return;

            _tokenSource.Cancel();
            StopDeviceServer();
            m_Task_DataSave.Stop();
            _running = false;
        }

        public void Restart()
        {
            Stop();
            _restart = true;
            _tokenSource = null;
            //Start();
        }
    }
}
